using System;
using System.Text;
using System.Threading;
using Mina.Core.Buffer;
using Mina.Core.Session;
using Mina.Filter.Codec;

namespace Com.FirstSolver.Splash;

public class FaceReaderDecoderAdapter : CumulativeProtocolDecoder
{
	private static readonly AttributeKey KEY_DECRYPTOR = new AttributeKey(typeof(FaceReaderDecoderAdapter), "Decryptor");

	internal static readonly AttributeKey KEY_SM2KEYEXCHANGEINFORMATION = new AttributeKey(typeof(FaceReaderDecoderAdapter), "SM2KeyExchangeInformation");

	internal static readonly AttributeKey KEY_SM2OWNERID = new AttributeKey(typeof(FaceReaderDecoderAdapter), "SM2OwnerId");

	protected readonly bool IsPassiveEncryption;

	protected readonly ManualResetEvent EncryptionReadyEvent;

	private static readonly byte[] AKE_KEY = new byte[16]
	{
		212, 90, 67, 230, 107, 92, 230, 36, 243, 24,
		164, 172, 206, 175, 95, 233
	};

	private static readonly byte[] AKE_IV = new byte[16]
	{
		210, 27, 117, 253, 74, 97, 230, 175, 3, 130,
		155, 117, 129, 22, 140, 236
	};

	public FaceReaderDecoderAdapter(bool isPassiveEncryption = false, ManualResetEvent encryptionReadyEvent = null)
	{
		IsPassiveEncryption = isPassiveEncryption;
		EncryptionReadyEvent = encryptionReadyEvent;
		encryptionReadyEvent?.Reset();
	}

	protected override bool DoDecode(IoSession session, IoBuffer input, IProtocolDecoderOutput output)
	{
		if (input.Remaining >= 4)
		{
			input.Mark();
			byte[] SizeByteArray = new byte[4];
			input.Get(SizeByteArray, 0, 4);
			int Size = Utils.ToInt32(SizeByteArray, 0, ByteOrder.BIG_ENDIAN);
			if (Size == 0)
			{
				return input.HasRemaining;
			}
			if (input.Remaining >= Size)
			{
				byte[] Source = new byte[Size];
				input.Get(Source, 0, Size);
				SM4 Decryptor = session.GetAttribute(KEY_DECRYPTOR) as SM4;
				if (Decryptor != null)
				{
					Source = Decryptor.TransformFinalBlock(Source, 0, Size);
				}
				string Message = Encoding.UTF8.GetString(Source);
				UNION_PARSE_TYPE A = Message.FromJsonTo<UNION_PARSE_TYPE>();
				if (string.Equals(A.COMMAND, "AKE") && Decryptor == null)
				{
					SetLicense(session, Message);
				}
				else if (string.Equals(A.RETURN, "AKE") && Decryptor == null)
				{
					GetLicense(session, Message);
				}
				else if (IsPassiveEncryption && Decryptor == null)
				{
					session.Close(rightNow: true);
				}
				else
				{
					output.Write(Message);
				}
				return input.HasRemaining;
			}
			input.Reset();
		}
		return false;
	}

	public override void Dispose(IoSession session)
	{
		((IDisposable)session.GetAttribute(KEY_DECRYPTOR))?.Dispose();
		session.RemoveAttribute(KEY_DECRYPTOR);
		session.RemoveAttribute(KEY_SM2KEYEXCHANGEINFORMATION);
		session.RemoveAttribute(KEY_SM2OWNERID);
		base.Dispose(session);
	}

	public static void SetSecretKey(IoSession session, byte[] secretKey, int mode)
	{
		if (session.GetAttribute(KEY_DECRYPTOR) is SM4 Decryptor)
		{
			Decryptor.Dispose();
			session.RemoveAttribute(KEY_DECRYPTOR);
		}
		if (secretKey != null)
		{
			SM4 Decryptor2 = new SM4(CipherDirection.Decryption, CipherMode.OFB);
			byte[] DerivedVector = ((mode != 0 || secretKey.Length != 32) ? SM3.KDF(secretKey, 32) : secretKey);
			byte[] Key = new byte[16];
			Array.Copy(DerivedVector, 0, Key, 0, 16);
			byte[] IV = new byte[16];
			Array.Copy(DerivedVector, 16, IV, 0, 16);
			Decryptor2.Initialize(Key, IV);
			session.SetAttribute(KEY_DECRYPTOR, Decryptor2);
		}
	}

	private static void SetLicense(IoSession session, string message)
	{
		REPLY_AKE_TYPE M = new REPLY_AKE_TYPE();
		M.RETURN = "AKE";
		M.PARAM = new PARAM_REPLY_AKE_TYPE();
		M.PARAM.result = "success";
		M.PARAM.sn = session.GetAttribute(KEY_SM2OWNERID) as string;
		COMMAND_AKE_TYPE A = message.FromJsonTo<COMMAND_AKE_TYPE>();
		if (A.PARAM == null || A.PARAM.key == null)
		{
			Random R = new Random();
			int Length = R.Next(16, 33);
			StringBuilder sb = new StringBuilder(Length);
			for (int i = 0; i < Length; i++)
			{
				sb.Append(char.ConvertFromUtf32(R.Next(33, 127)));
			}
			byte[] SecretKey = Encoding.UTF8.GetBytes(sb.ToString());
			using (SM4 sm = new SM4(CipherDirection.Encryption, CipherMode.OFB))
			{
				sm.Initialize(AKE_KEY, AKE_IV);
				M.PARAM.key = new string[2]
				{
					Base64.Encode(sm.TransformFinalBlock(SecretKey, 0, SecretKey.Length)),
					null
				};
				session.Write(M.ToJsonString());
			}
			FaceReaderProtocolCodecFactory.SetEncoderKey(session, SecretKey, 1);
			FaceReaderProtocolCodecFactory.SetDecoderKey(session, SecretKey, 1);
		}
		else if (session.GetAttribute(KEY_SM2KEYEXCHANGEINFORMATION) is SM2KeyExchangeInformation Information)
		{
			SM2 sm2 = FaceReaderProtocolCodecFactory.sm2;
			string PartnerPublicKey = A.PARAM.key[0];
			BigInteger X = new BigInteger(PartnerPublicKey.Substring(0, 64), 16);
			BigInteger Y = new BigInteger(PartnerPublicKey.Substring(64), 16);
			Information.PartnerPublicKey = new FpPoint(sm2.mCurve, sm2.mCurve.FromBigInteger(X), sm2.mCurve.FromBigInteger(Y), withCompression: false);
			Information.PartnerZ = sm2.ComputeZ(Encoding.UTF8.GetBytes(A.PARAM.sn), Information.PartnerPublicKey);
			PartnerPublicKey = A.PARAM.key[1];
			X = new BigInteger(PartnerPublicKey.Substring(0, 64), 16);
			Y = new BigInteger(PartnerPublicKey.Substring(64), 16);
			Information.PartnerR = new FpPoint(sm2.mCurve, sm2.mCurve.FromBigInteger(X), sm2.mCurve.FromBigInteger(Y), withCompression: false);
			if (sm2.KeyAgreement(Information, IsInitiator: false, 32, out var SharedKey, withConfirm: false))
			{
				session.RemoveAttribute(KEY_SM2KEYEXCHANGEINFORMATION);
				session.RemoveAttribute(KEY_SM2OWNERID);
				M.PARAM.key = new string[2];
				StringBuilder sb2 = new StringBuilder(128);
				sb2.Append(Utils.ToString(sm2.GetEncoded(Information.PublicKey.mX)));
				sb2.Append(Utils.ToString(sm2.GetEncoded(Information.PublicKey.mY)));
				M.PARAM.key[0] = sb2.ToString();
				sb2.Clear();
				sb2.Append(Utils.ToString(sm2.GetEncoded(Information.R.mX)));
				sb2.Append(Utils.ToString(sm2.GetEncoded(Information.R.mY)));
				M.PARAM.key[1] = sb2.ToString();
				session.Write(M.ToJsonString());
				FaceReaderProtocolCodecFactory.SetEncoderKey(session, SharedKey, 0);
				FaceReaderProtocolCodecFactory.SetDecoderKey(session, SharedKey, 0);
			}
		}
	}

	private void GetLicense(IoSession session, string message)
	{
		REPLY_AKE_TYPE A = message.FromJsonTo<REPLY_AKE_TYPE>();
		if (!string.Equals(A.PARAM.result, "success"))
		{
			return;
		}
		if (A.PARAM.key.Length < 2)
		{
			byte[] Source = Base64.Decode(A.PARAM.key[0]);
			using SM4 sm = new SM4(CipherDirection.Decryption, CipherMode.OFB);
			sm.Initialize(AKE_KEY, AKE_IV);
			byte[] SecretKey = sm.TransformFinalBlock(Source, 0, Source.Length);
			FaceReaderProtocolCodecFactory.SetEncoderKey(session, SecretKey, 1);
			FaceReaderProtocolCodecFactory.SetDecoderKey(session, SecretKey, 1);
			EncryptionReadyEvent.Set();
			return;
		}
		SM2KeyExchangeInformation Information = session.GetAttribute(KEY_SM2KEYEXCHANGEINFORMATION) as SM2KeyExchangeInformation;
		SM2 sm2 = FaceReaderProtocolCodecFactory.sm2;
		string PartnerPublicKey = A.PARAM.key[0];
		BigInteger X = new BigInteger(PartnerPublicKey.Substring(0, 64), 16);
		BigInteger Y = new BigInteger(PartnerPublicKey.Substring(64), 16);
		Information.PartnerPublicKey = new FpPoint(sm2.mCurve, sm2.mCurve.FromBigInteger(X), sm2.mCurve.FromBigInteger(Y), withCompression: false);
		Information.PartnerZ = sm2.ComputeZ(Encoding.UTF8.GetBytes(A.PARAM.sn), Information.PartnerPublicKey);
		PartnerPublicKey = A.PARAM.key[1];
		X = new BigInteger(PartnerPublicKey.Substring(0, 64), 16);
		Y = new BigInteger(PartnerPublicKey.Substring(64), 16);
		Information.PartnerR = new FpPoint(sm2.mCurve, sm2.mCurve.FromBigInteger(X), sm2.mCurve.FromBigInteger(Y), withCompression: false);
		if (sm2.KeyAgreement(Information, IsInitiator: true, 32, out var SharedKey, withConfirm: false))
		{
			session.RemoveAttribute(KEY_SM2KEYEXCHANGEINFORMATION);
			FaceReaderProtocolCodecFactory.SetEncoderKey(session, SharedKey, 0);
			FaceReaderProtocolCodecFactory.SetDecoderKey(session, SharedKey, 0);
			EncryptionReadyEvent.Set();
		}
	}
}
